package com.hmy.finance.service.statement.statementHandlerFactory;


import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.hmy.ccp.error.exception.HMYRuntimeException;
import com.hmy.finance.api.bo.core.request.enums.StatementTypeEnum;
import com.hmy.finance.service.statement.dto.CalculateStatementDeviceDto;
import com.hmy.finance.service.statement.dto.CreateStatementDeviceDto;
import com.hmy.finance.service.statement.dto.CreateStatementDto;
import com.hmy.finance.service.statement.dto.ReconciliationStatementDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


/**
 * 月账单拉取策略
 */
@Slf4j
@Component
public class MonthStatementHandler extends DefaultStatementHandler {


    /**
     * 月结账单拉取策略,目前使用默认处理器，若策略单独改变，则重写此方法即可
     */
    @Override
    public ReconciliationStatementDto doStatementHandler(CreateStatementDto createStatementDto) {
        /*step1.根据参数里的开始时间和结束时间进行时间校验*/
        List<CalculateStatementDeviceDto> errorStatementDevices = new ArrayList<>();//错误数据list
        createStatementDto.getDeviceDetails().forEach(d -> {
            /*月结模式下设置设备预开始&结束时间为账单的开始&结束时间*/
            d.setSetPreStartTime(createStatementDto.getStatementStartTime());
            d.setSetPreEndTime(createStatementDto.getStatementEndTime());//设置设备纬度予定的账单开始时间和结束时间

            LocalDateTime lastReconciliationTime = d.getLastReconciliationTime();
            log.info("上次对账时间信息：ContractNo：" + d.getContractNo() + "；deviceNo:" + d.getDeviceNo() + ";lastReconciliationTime:" + d.getLastReconciliationTime());
            if (Objects.isNull(lastReconciliationTime)) {
                throw new HMYRuntimeException(506300002, new Object[]{d.getContractNo(), d.getDeviceNo()});//直接提示异常，需要开发排查为什么没有最近对账时间：默认为起租时间。
            } else {
                /*step1.1:过滤这个设备的最近一次账单截止日期和传入的开始时间&结束时间是否满足拉取账单条件
                 * 开始时间需要在最近一次对账时间之前或等于，结束时间需要在最近一次对账时间之后或等于
                 * */
                Boolean isBetween = isLastReconciliationTimeBetween(d.getLastReconciliationTime(), createStatementDto.getStatementStartTime(), createStatementDto.getStatementEndTime());
                if (isBetween) {
                    /*step1.2:需要计算并设定设备当前账期的开始时间和结束时间。*/
                    this.calculateStatementDeviceCycle(d);//计算该设备的当前账期的开始时间和结束时间。
                } else {
                    //step1.3:如果不在时间之内，则添加到错误提示list中.期结&月结，总账单不需要这个判断。
                    CalculateStatementDeviceDto errorStatementDeviceDto = new CalculateStatementDeviceDto();
                    errorStatementDeviceDto.setDeviceNo(d.getDeviceNo());
                    errorStatementDeviceDto.setContractNo(d.getContractNo());
                    errorStatementDevices.add(errorStatementDeviceDto);
                }
            }
        });
        /*step.2.如果错误数据list不为空，则抛出异常，如果不为空，则继续处理*/
        if (CollectionUtil.isNotEmpty(errorStatementDevices)) {
            StringBuffer errorDeviceStr = new StringBuffer();
            errorStatementDevices.forEach(errorStatementDeviceDto -> {
                errorDeviceStr.append("合同号:").append(errorStatementDeviceDto.getContractNo()).append(";设备号:").append(errorStatementDeviceDto.getDeviceNo()).append(";");
            });
            // 抛出异常
            throw new HMYRuntimeException(506300001, new Object[]{errorDeviceStr});
        }
        return defaultStatementHandler(createStatementDto);
    }

    /**
     * 计算账单周期
     */
    @Override
    public void calculateStatementCycle(CalculateStatementDeviceDto statementDevice) {
        log.info("calculateStatementCycle request:{}", JSON.toJSONString(statementDevice));
//        if (statementDevice.getLastReconciliationTime().isEqual(statementDevice.getDeviceBeginRentTime())) {
//            //如果是首次，则去上次截止时间-即起租时间
//            statementDevice.setCurrentRentalStartTime(statementDevice.getLastReconciliationTime());
//        } else {
//            //如果不是首次，则去上次截止时间+1秒
//            statementDevice.setCurrentRentalStartTime(statementDevice.getLastReconciliationTime().plusSeconds(1));
//        }
        statementDevice.setCurrentRentalStartTime(statementDevice.getLastReconciliationTime());
        statementDevice.setCurrentRentalEndTime(calculateStatementEndTime(statementDevice.getLastReconciliationTime(), statementDevice.getDeviceEndRentTime()));
        log.info("calculateStatementCycle response:{}", JSON.toJSONString(statementDevice));
    }

    /**
     * 计算设备纬度的时间周期
     */
    @Override
    public void calculateStatementDeviceCycle(CreateStatementDeviceDto statementDevice) {
        log.info("calculateStatementCycle request:{}", JSON.toJSONString(statementDevice));
        statementDevice.setCurrentRentalStartTime(statementDevice.getLastReconciliationTime());
        statementDevice.setCurrentRentalEndTime(calculateDeviceEndTime(statementDevice.getSetPreEndTime(), statementDevice.getDeviceEndRentTime()));
        log.info("calculateStatementCycle response:{}", JSON.toJSONString(statementDevice));
    }

    /**
     * 计算账单周期结束时间
     *
     * @param lastReconciliationTime 本次对账设备中最晚的上次对账截止时间(单设备就是此设备的上次对账截止时间)
     * @param deviceEndRentTime      设备退场时间,可能为null
     *                               //     * @param setStatementEndTime    本地账单结束时间
     * @return 该设备本次账单截止时间
     */
    public static LocalDateTime calculateStatementEndTime(LocalDateTime lastReconciliationTime, LocalDateTime deviceEndRentTime) {
        //lastReconciliationTime往后延1天作为本次的起始时间
        //对比lastReconciliationTime和LocalDateTime.now(),如果两者在同一个月，则返回当前月的最后一天，如果不在同一个月，则返回lastReconciliationTime所在月的最后一天
        LocalDateTime lastDayOfMonth;
        if (LocalDateTime.now().getYear() <= lastReconciliationTime.getYear() && LocalDateTime.now().getMonth().getValue() <= lastReconciliationTime.getMonth().getValue()) {
            lastDayOfMonth = lastReconciliationTime.plusMonths(1).withDayOfMonth(1).minusDays(1).withHour(23).withMinute(59).withSecond(59).truncatedTo(java.time.temporal.ChronoUnit.SECONDS);
        } else {
            /*计算上个月的最后一天*/
            lastDayOfMonth = LocalDateTime.now().minusMonths(1).plusMonths(1).withDayOfMonth(1).minusDays(1).withHour(23).withMinute(59).withSecond(59).truncatedTo(java.time.temporal.ChronoUnit.SECONDS);
        }

        if (Objects.isNull(deviceEndRentTime)) {
            return lastDayOfMonth;//如果设备退租时间为空，则返回本月最后一天
        }
        LocalDateTime endTime = deviceEndRentTime.isBefore(lastDayOfMonth) ? deviceEndRentTime : lastDayOfMonth;
        return endTime;
    }

    /**
     * @param preEndTime        本次账单预结束时间(前端用户选择的结束时间)
     * @param deviceEndRentTime 设备退场时间，可能为null
     * @return 设备本期账单结束时间
     */
    public  LocalDateTime calculateDeviceEndTime(LocalDateTime preEndTime, LocalDateTime deviceEndRentTime) {
        LocalDateTime endTime;
        if (Objects.isNull(deviceEndRentTime)) {
            endTime = preEndTime;//如果设备退租时间为空，则返回预定的结束时间
        } else {
            endTime = preEndTime.isBefore(deviceEndRentTime) ? preEndTime : deviceEndRentTime;
        }
        return endTime;
    }

    @Override
    public StatementTypeEnum getStatementEnumType() {
        return StatementTypeEnum.MONTHLY;
    }
}
